home *** CD-ROM | disk | FTP | other *** search
/ SuperHack / SuperHack CD.bin / CODING / GRAPHICS / MCGA_TUT.ZIP / MCGA.04 < prev    next >
Encoding:
Text File  |  1995-08-31  |  11.8 KB  |  369 lines

  1. -------------------------------------------------------------------------------
  2. Nachricht Nr. 0236 aus Area PASCAL.GER   Exportiert mit Yuppie! v2.10
  3. -------------------------------------------------------------------------------
  4. Datum: 02 Jul 92  09:14:21
  5. Von  : Thomas Karp                            
  6. An   : All                                    
  7. Betr.: MCGA TUTORIAL                                                       
  8. -------------------------------------------------------------------------------
  9. und weiter
  10.                     
  11. An   : All                                    
  12. Betr.: MCGA Tutorial #4                                                    
  13.  
  14.                            MCGA Graphics Tutorial
  15.                                  Lesson #4
  16.                                 by Jim Cook
  17.  
  18. Now for the fun stuff.........
  19. Whenever you design a graphics package you have to decide what format to
  20. store your images in.  There are too many to count on both hands, and
  21. for the topic of this lesson I have to come up with one that is easy to
  22. handle and can be represented quickly.  Here's what I've come up with...
  23. NO format.  We are going to basically emulate the PutImage and GetImage
  24. found in TP's BGI.  I chose this because it's easiest to understand and
  25. it can be put on the screen very quickly.  As a matter of fact we can
  26. put a complete screen image up 32 times per second on my 386/33mhz.
  27. Most of the games we write will rely on a much smaller animation scale,
  28. but may involve up to 100 animations at a time.
  29.  
  30. OK, you may ask, how do I save an image to disk in an uncompressed
  31. format?  Good question, most paint programs do not directly support this
  32. format, but most do support PCX files.  We will write a program that
  33. puts a .PCX (or it's smaller cousin .PCC) graphic on the screen.  From
  34. here we will cut the chunk of the picture we want and save it to the
  35. disk.  In order to accomplish all of this, we need to understand PCX
  36. files.  Fasten your seatbelts!
  37.  
  38.   PCXHeader   =  record
  39.                    Signature      :  Byte;    { $0A }
  40.                    Version        :  Byte;    { varies }
  41.                    Encoding       :  Byte;    { $01 }
  42.                    BitsPerPixel   :  Byte;    { $08 for 1 byte per pixel }
  43.                    XMin,YMin,                 { Upper-left corner (0,0)  }
  44.                    XMax,YMax      :  Integer; { Lower-right corner }
  45.                    HRes,VRes      :  Integer; { resolution 320 x 200 }
  46.                    Palette        :  Array [0..47] of byte;  { colors }
  47.                    Reserved       :  Byte;
  48.                    Planes         :  Byte;
  49.                    BytesPerLine   :  Integer;
  50.                    PaletteType    :  Integer;
  51.                    Filler         :  Array [0..57] of byte;
  52.                  end;
  53.  
  54. This is the first 128 bytes of any PCX (or PCC) file.  The Signature is
  55. always $0A and the Encoding is always $01, at least for now.  The rest
  56. of the header is self-explanatory.  Well, there is one catch, when this
  57. format was developed, the geniuses at ZSoft didn't plan for pictures
  58. with 256 colors or more.  There are 3 bytes per color in MCGA mode so
  59. the measely 48 bytes set aside in the header only allows for 16 colors.
  60. ZSoft ended up putting the 768 bytes of palette data at the end of the
  61. file and the Palette area in the header is available real estate.
  62.  
  63. Immediately following the header is the body of the PCX file.  The body
  64. is compressed by a technique called run-length compression.  For many of
  65. you, this is the first time you have looked at compression so I'll take
  66. this slow.  It is not important to understand this for the purpose of
  67. our graphic library, but it is vital if you work with other formats.
  68. Most use some type of run-length compression.  TIFF and GIF use LZW
  69. compression, by the way.
  70.  
  71. This is the flowchart (ugh) for unpacking one row of a PCX file:
  72.  
  73.            -------->  Read in a byte X
  74.          /                   |
  75.        /           Are the two high bits set?
  76.      /          [ 1  1  ?  ?  ?  ?  ?  ? ]  <- binary representation of X
  77.    /               / Yes                \ No
  78.    |        X := X and $3F         Write X to screen
  79.    |              |                      |
  80.    ^       Get next byte Y               |
  81.    |              |                      |
  82.    |       Write byte Y to the          /
  83.    |       screen X times             /
  84.    |              \                 /
  85.     \                Done with row?
  86.       \                | No   | Yes
  87.         \              /      |
  88.           -----<------        | Exit
  89.  
  90. That's how to unpack 1 row of a PCX, to unpack the whole picture:
  91.  
  92.                 For I := 1 to NumRows do UnpackRow;
  93.  
  94. Along with some housekeeping to keep track of advancing down the screen
  95. memory.  I know you want the code so here goes:
  96.  
  97. Unit Lib04;
  98.  
  99. interface
  100.  
  101. type
  102.   PCXHeaderPtr=  ^PCXHeader;
  103.   PCXHeader   =  record
  104.                    Signature      :  Char;
  105.                    Version        :  Char;
  106.                    Encoding       :  Char;
  107.                    BitsPerPixel   :  Char;
  108.                    XMin,YMin,
  109.                    XMax,YMax      :  Integer;
  110.                    HRes,VRes      :  Integer;
  111.                    Palette        :  Array [0..47] of byte;
  112.                    Reserved       :  Char;
  113.                    Planes         :  Char;
  114.                    BytesPerLine   :  Integer;
  115.                    PaletteType    :  Integer;
  116.                    Filler         :  Array [0..57] of byte;
  117.                  end;
  118.  
  119. Procedure DisplayPCXPas (X,Y:Integer;Buf:Pointer);
  120. Procedure DisplayPCX    (X,Y:Integer;Buf:Pointer);
  121.  
  122. implementation
  123.  
  124. uses
  125.   Dos;
  126.  
  127. Procedure ExtractLine (BytesWide:Integer;Var Source,Dest:Pointer);
  128. var
  129.   DestIdx,
  130.   SourceIdx   :  Integer;
  131.   InCode,
  132.   RunCount    :  Byte;
  133. begin
  134.   DestIdx := 0;
  135.   SourceIdx := 0;
  136.  
  137.   While DestIdx < BytesWide do begin
  138.     InCode := Mem [Seg(Source^):Ofs(Source^)+SourceIdx];
  139.     Inc (SourceIdx);
  140.  
  141.     If (InCode and $C0) = $C0 then begin
  142.       RunCount := InCode and $3F;
  143.       InCode := Mem [Seg(Source^):Ofs(Source^)+SourceIdx];
  144.       Inc (SourceIdx);
  145.       FillChar (Mem[Seg(Dest^):Ofs(Dest^)+DestIdx],RunCount,InCode);
  146.       Inc (DestIdx,RunCount);
  147.       end
  148.     Else begin
  149.       Mem [Seg(Dest^):Ofs(Dest^)+DestIdx] := InCode;
  150.       Inc (DestIdx);
  151.       end;
  152.     end;
  153.   If Odd (BytesWide) then Source :=  Ptr(Seg(Source^),Ofs(Source^)+SourceIdx+2)
  154.                      else Source :=  Ptr(Seg(Source^),Ofs(Source^)+SourceIdx);
  155.   Dest   := Ptr(Seg(Dest^),Ofs(Dest^)+DestIdx);
  156. end;
  157.  
  158. Procedure ExtractLineASM (BytesWide:Integer;Var Source,Dest:Pointer);
  159. var
  160.   DestSeg,
  161.   DestOfs,
  162.   SourceSeg,
  163.   SourceOfs   :  Word;
  164. begin
  165.   SourceSeg := Seg (Source^);
  166.   SourceOfs := Ofs (Source^);
  167.   DestSeg   := Seg (Dest^);
  168.   DestOfs   := Ofs (Dest^);
  169.  
  170.   asm
  171.     push  ds
  172.     push  si
  173.  
  174.     mov   ax,DestSeg
  175.     mov   es,ax
  176.     mov   di,DestOfs     { es:di -> destination pointer }
  177.     mov   ax,SourceSeg
  178.     mov   ds,ax
  179.     mov   si,SourceOfs   { ds:si -> source buffer }
  180.  
  181.     mov   bx,di
  182.     add   bx,BytesWide   { bx holds position to stop for this row }
  183.     xor   cx,cx
  184.  
  185.   @@GetNextByte:
  186.     cmp   bx,di          { are we done with the line }
  187.     jbe   @@ExitHere
  188.  
  189.     lodsb                { al contains next byte }
  190.  
  191.     mov   ah,al
  192.     and   ah,0C0h
  193.     cmp   ah,0C0h
  194.     jne   @@SingleByte
  195.                          { must be a run of bytes }
  196.     mov   cl,al
  197.     and   cl,3Fh
  198.     lodsb
  199.     rep   stosb
  200.     jmp   @@GetNextByte
  201.  
  202.   @@SingleByte:
  203.     stosb
  204.     jmp   @@GetNextByte
  205.  
  206.   @@ExitHere:
  207.     mov   SourceSeg,ds
  208.     mov   SourceOfs,si
  209.     mov   DestSeg,es
  210.     mov   DestOfs,di
  211.  
  212.     pop   si
  213.     pop   ds
  214.   end;
  215.  
  216.   If Odd(BytesWide) then Source := Ptr (SourceSeg,SourceOfs+2)
  217.                     else Source := Ptr (SourceSeg,SourceOfs);
  218.  
  219.   Dest := Ptr (DestSeg,DestOfs);
  220. end;
  221.  
  222. Procedure DisplayPCX (X,Y:Integer;Buf:Pointer);
  223. var
  224.   I,NumRows,
  225.   BytesWide   :  Integer;
  226.   Header      :  PCXHeaderPtr;
  227.   DestPtr     :  Pointer;
  228.   Offset      :  Word;
  229. begin
  230.   Header := Ptr (Seg(Buf^),Ofs(Buf^));
  231.   Buf := Ptr (Seg(Buf^),Ofs(Buf^)+128);
  232.   Offset := Y * 320 + X;
  233.   NumRows := Header^.YMax - Header^.YMin + 1;
  234.   BytesWide := Header^.XMax - Header^.XMin + 1;
  235.   For I := 1 to NumRows do begin
  236.     DestPtr := Ptr ($A000,Offset);
  237.     ExtractLineASM (BytesWide,Buf,DestPtr);
  238.     Inc (Offset,320);
  239.     end;
  240. end;
  241.  
  242. Procedure DisplayPCXPas (X,Y:Integer;Buf:Pointer);
  243. var
  244.   I,NumRows,
  245.   BytesWide   :  Integer;
  246.   Header      :  PCXHeaderPtr;
  247.   DestPtr     :  Pointer;
  248.   Offset      :  Word;
  249. begin
  250.   Header := Ptr (Seg(Buf^),Ofs(Buf^));
  251.   Buf := Ptr (Seg(Buf^),Ofs(Buf^)+128);
  252.   Offset := Y * 320 + X;
  253.   NumRows := Header^.YMax - Header^.YMin + 1;
  254.   BytesWide := Header^.XMax - Header^.XMin + 1;
  255.   For I := 1 to NumRows do begin
  256.     DestPtr := Ptr ($A000,Offset);
  257.     ExtractLine (BytesWide,Buf,DestPtr);
  258.     Inc (Offset,320);
  259.     end;
  260. end;
  261. end.
  262.  
  263. That's the unit to put a PCX on the screen.  There are two routines to
  264. put up a picture.  One is written entirely in Pascal and the other is an
  265. assembly/Pascal hybrid.  I was very suprised to find the hybrid
  266. performed as well as the 100% assembly routine I wrote.  Following is a
  267. routine to put the unit to the test.
  268.  
  269. Program MCGATest;
  270.  
  271. uses
  272.   Crt,Dos,MCGALib,MCGAPCX;
  273.  
  274. var
  275.   Stop,
  276.   Start       :  LongInt;
  277.   Regs        :  Registers;
  278.   PicBuf      :  Pointer;
  279. const
  280.   NumTimes    = 10;
  281.  
  282. Procedure LoadBuffer (S:String;Buf:Pointer);
  283. var
  284.   F           :  File;
  285.   BlocksRead  :  Word;
  286. begin
  287.   Assign (F,S);
  288.   Reset (F,1);
  289.   BlockRead (F,Buf^,65000,BlocksRead);
  290.   Close (F);
  291. end;
  292.  
  293. Function Tick : LongInt;
  294. begin
  295.   Regs.ah := 0;
  296.   Intr ($1A,regs);
  297.   Tick := Regs.cx shl 16 + Regs.dx;
  298. end;
  299.  
  300. Procedure ShowAndTell;
  301. var
  302.   Ch     :  Char;
  303. begin
  304.   TextMode (3);
  305.   WriteLn ('We just displayed a PCX file to the screen ',NumTimes,'  times.');
  306.   WriteLn ('Routine took ',(Stop-Start)/NumTimes:6:4,' ticks or ');
  307.   WriteLn ((Stop-Start)/18.2/NumTimes:4:3,' seconds per image!');
  308.   WriteLn ('That''s about ',18.2/((Stop-Start)/NumTimes):6:4,' times per
  309.   second.'); Repeat Until Keypressed;
  310.   While Keypressed do Ch := Readkey;
  311. end;
  312.  
  313. Procedure Control;
  314. var
  315.   I :  Integer;
  316. begin
  317.   SetGraphMode ($13);
  318.   LoadBuffer ('',PicBuf);    {Hier muesst Ihr ein PCX-File eintragen}
  319.   Start := Tick;
  320.   For I := 1 to NumTimes do
  321.     DisplayPCXPas (0,0,PicBuf);
  322.   Stop := Tick;
  323.   ShowAndTell;
  324.  
  325.   SetGraphMode ($13);
  326.   Start := Tick;
  327.   For I := 1 to NumTimes do
  328.     DisplayPCX (0,0,PicBuf);
  329.   Stop := Tick;
  330.   ShowAndTell;
  331. end;
  332.  
  333. Procedure Init;
  334. begin
  335.   Randomize;
  336.   GetMem (PicBuf,65500);
  337. end;
  338.  
  339. Begin
  340.   Init;
  341.   Control;
  342. End.
  343.  
  344. Please run the above program and drop me a line on how it performs.  I
  345. am very curious to learn how it behave on different machines.  Leave me
  346. a message showing CPU, speed and how many images per second you
  347. obtained.  Kinda like this:
  348.  
  349.                           <----- 386, 33 mhz ----->
  350.         Pascal method ............................. 0.8585 per second
  351.         Pascal/Asm hybrid.......................... 8.6667 per second
  352.  
  353. The first thing you will notice is, we are not setting the palette.  The
  354. picture will appear in an ugly color unless the PCX you try to display
  355. is drawn using the default palette.  Also, it does not support pictures
  356. larger then 65500 bytes.  I'll finish up this discussion next time.
  357.  
  358. Hack on,
  359. jim  
  360.  
  361. --- Yuppie! v2.10
  362.  * Origin: ...:-)..alles eine Frage des richtigen Fehlers... (2:245/555.10)
  363. SEEN-BY: 241/2 2000 4000 4150 4500 5000 5100 5300 5600 5900 7000 7003
  364. SEEN-BY: 241/7005 7020 7042 7200 7300 7400 7503 7600 7903 243/10 16
  365. SEEN-BY: 243/20 22 91 244/10 11 20 21 24 26 28 37 40 50 70 90 245/2
  366. SEEN-BY: 245/5 51 53 54 55 56 57 505 551 555 5600 246/2 247/1 36 700
  367. SEEN-BY: 248/1 404 249/1 9 15 2405/1 3 4 9 10 13 44 100 110 999
  368.  
  369.